/**
 * 
 */
package org.oproject.framework.orm.ibatis.config;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;

import net.sf.cglib.core.ReflectUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.oproject.framework.orm.ibatis.bytecode.DynamicDAOImplClassGenerator;
import org.oproject.framework.orm.ibatis.bytecode.codegenerator.method.MethodGenerator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;


/**
 * <p>
 * <strong>Ibatis动态数据访问层工厂bean</strong>
 * </p>
 * <p>
 * 基于cglib实现，使用动态字节码增强技术在运行时根据DAO接口定义实现类。
 * </p>
 * @see org.springframework.beans.factory.FactoryBean<Object>
 * @author aohai.li
 * @version DSPS, 2010-11-9
 * @since DSPS
 */
public class IbatisDynamicDAOFactoryBean implements FactoryBean<Object>, ApplicationContextAware {

	private static final Log log = LogFactory.getLog(IbatisDynamicDAOFactoryBean.class);
	
	/**
	 * 接口类型
	 */
	private Class<?> interfaceClass;
	
	/**
	 * ibatis的sqlMapClient
	 */
	private Object sqlMapClient;
	
	/**
	 * 上下文
	 */
	private ApplicationContext ac;
	
	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.FactoryBean#getObject()
	 */
	@Override
	public Object getObject() throws Exception {
		if(log.isDebugEnabled()){
			log.debug("加载接口：" + interfaceClass.getName());
		}
		Class<?> clazz = null;
		try{
			clazz = Class.forName(interfaceClass.getName() + "$Impl");
		}catch(ClassNotFoundException e){
			Collection<MethodGenerator> methodGenerators = ac.getBeansOfType(MethodGenerator.class).values();
			DynamicDAOImplClassGenerator classGenerator = new DynamicDAOImplClassGenerator(interfaceClass.getName(), interfaceClass.getName() + "$Impl");
			classGenerator.initClass();
			classGenerator.addSqlMapClientProperty(sqlMapClient.getClass(), "sqlMapClient");
			classGenerator.addImplementsMethod(methodGenerators);
			classGenerator.defineClass();
			clazz = Class.forName(interfaceClass.getName() + "$Impl");
		}
		Object obj = ReflectUtils.newInstance(clazz);
		if(log.isDebugEnabled()){
			log.debug("动态创建类：" + clazz.getName());
			if(null != clazz.getMethods()){
				final StringBuilder methodsSB = new StringBuilder();
				for(Method method:clazz.getMethods()){
					methodsSB.append(method.getName()).append(", ");
				}
				log.debug(" method:{" + methodsSB.toString() + "}");
			}
			if(null != clazz.getDeclaredFields()){
				final StringBuilder fieldsSB = new StringBuilder();
				for(Field field:clazz.getDeclaredFields()){
					fieldsSB.append(field.getName()).append(field.getType().getName()).append(", ");
				}
				log.debug(" fields:{" + fieldsSB.toString() + "}");
			}
		}
		Method setter = clazz.getMethod("setSqlMapClient", new Class<?>[]{sqlMapClient.getClass()});
		setter.invoke(obj, sqlMapClient);
		return obj;
	}

	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.FactoryBean#getObjectType()
	 */
	@Override
	public Class<?> getObjectType() {
		return interfaceClass;
	}

	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.FactoryBean#isSingleton()
	 */
	@Override
	public boolean isSingleton() {
		return false;
	}

	public void setInterface(String interfaceName) {
		try {
			this.interfaceClass = Class.forName(interfaceName);
		} catch (ClassNotFoundException e) {
			throw new BeanCreationException(e.toString());
		}
	}

	/* (non-Javadoc)
	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
	 */
	@Override
	public void setApplicationContext(ApplicationContext arg0)
			throws BeansException {
		this.ac = arg0;
	}

	public void setSqlMapClient(Object sqlMapClient) {
		this.sqlMapClient = sqlMapClient;
	}
}
